package com.own.component.security.aspect;

import com.own.aop.login.LoginIgnore;
import com.own.component.autoconfig.property.security.SecurityProperties;
import com.own.component.common.base.controller.BaseController;
import com.own.component.common.login.util.SessionUserUtil;
import com.own.component.token.base.RequestTokenManager;
import com.own.constant.ConstantSystemAopOrder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;

/**
 * LoginCheckAspect
 *
 * @author chenxueli
 * @date 2023-10-11 14:54:00
 */
@Slf4j
@Aspect
@Order(ConstantSystemAopOrder.LOGIN_CHECK)
@Component
@ConditionalOnProperty(prefix = SecurityProperties.PREFIX, name = "login-verification", havingValue = "true")
public class LoginCheckAspect {

    @Resource
    private HttpServletRequest request;

    @Resource
    private RequestTokenManager requestTokenManager;

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMapping() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void getMapping() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void postMapping() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void deleteMapping() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)")
    public void putMapping() {
    }

    @Before("requestMapping()")
    public void requestMappingBefore(JoinPoint point) {
        run(point);
    }

    @Before("getMapping()")
    public void getMappingBefore(JoinPoint point) {
        run(point);
    }

    @Before("postMapping()")
    public void postMappingBefore(JoinPoint point) {
        run(point);
    }

    @Before("deleteMapping()")
    public void deleteMappingBefore(JoinPoint point) {
        run(point);
    }

    @Before("putMapping()")
    public void putMappingBefore(JoinPoint point) {
        run(point);
    }

    /**
     * 执行的方法
     *
     * @param point 切点
     */
    private void run(JoinPoint point) {
        // 获取切点的方法
        var signature = (MethodSignature) point.getSignature();
        var method = signature.getMethod();
        var clazz = point.getTarget().getClass();
        // 判断父级是否是BaseController
        if (!isParentClassOfBaseController(clazz, BaseController.class)) {
            return;
        }
        // 判断是否需要验证登录
        if (method.isAnnotationPresent(LoginIgnore.class)) {
            return;
        }
        // 获取当前注解的class上的相关注解
        if (isParentClassHasAnnotation(clazz, LoginIgnore.class)) {
            return;
        }
        // 验证登录信息
        SessionUserUtil.checkLogin();
    }

    /**
     * 是否继承于目标类
     *
     * @param sourceClass 源类
     * @param targetClass 目标类
     * @return 是否继承
     */
    private boolean isParentClassOfBaseController(Class<?> sourceClass, Class<?> targetClass) {
        if (sourceClass == null || sourceClass == Object.class) {
            return false;
        }
        if (sourceClass == targetClass) {
            return true;
        }
        return isParentClassOfBaseController(sourceClass.getSuperclass(), targetClass);
    }

    /**
     * 目标类以及其父类是否含有目标注解
     *
     * @param sourceClass     源类
     * @param annotationClass 目标注解
     * @return 是否继承
     */
    private boolean isParentClassHasAnnotation(Class<?> sourceClass, Class<? extends Annotation> annotationClass) {
        if (sourceClass == null || sourceClass == Object.class) {
            return false;
        }
        return sourceClass.isAnnotationPresent(annotationClass) || isParentClassHasAnnotation(sourceClass.getSuperclass(), annotationClass);
    }

}
